library(magrittr) # quando der problema com o "%>%"
library('dplyr') # selecao e filtro de dados
library('geosphere') # localizacao geoespacial
library(lubridate) # datas, funções hour, month, wday
library(plotly) # plot dos gráficos
library(knitr) # usada pelo plotly
library(dummies) # cria colunas binárias para variáveis categóricas
library(scales) # normaliza dados rescalando para float de 0 a 1
library(randomForest) # cria rede neural para criar regressão de tempo de viagem
source('preprocessing.R')
source('mapa_calor_ny.R')
train <- read.csv("data_source/train.csv")
# pega apenas as 10 primeiras linhas para teste
df <- head(train, 10000)

Preparação dos dados

Região de saída

df$bairro_saida = mapply(define_bairro, df$pickup_longitude, df$pickup_latitude)
df$bairro_chegada = mapply(define_bairro, df$dropoff_longitude, df$dropoff_latitude)

Adiciona distância Euclidiana calculada a partir das coordenadas (arquivo Preprocessing.R)

df$dist_euclidiana = dist_eucl(df)

Adiciona distância de Manhattan calculada a partir das coordenadas (arquivo Preprocessing.R)

df$dist_manhattan = dist_manh(df)
df$velocidade = df$dist_manhattan / df$trip_duration

Prepara data e hora da paprtida (acho que não precisa da chegada)

Com isso é possível pegar horário de pico e dia da semana

df$pickup_hour <- hour(df$pickup_datetime)
df$pickup_month <- month(df$pickup_datetime)
df$pickup_weekdays <- wday(df$pickup_datetime)

Limpeza de corridas zeradas e limpa corridas muito longas

df %>%
  filter(df$dist_manhattan > 0.5) -> df
df %>%
  filter(df$trip_duration < 10000) -> df

Análises descritivas

Divisão das regiões que separamos em NY:

Regiões NY

Regiões NY

Quantidade de viagens por região de saída e chegada

df %>%
  group_by(bairro_saida) %>%
  count() -> data_plot
plot1 = plot_ly(data= data_plot, x= ~bairro_saida, y= ~n, type = 'bar')
df %>%
  group_by(bairro_chegada) %>%
  count() -> data_plot
plot2 = plot_ly(data= data_plot, x= ~bairro_chegada, y= ~n, type = 'bar')
subplot(plot1, plot2, shareY = T)

Média da velocidade das viagens por região de saída e chegada

df %>%
  group_by(bairro_saida) %>%
  summarize(velocidade_media = mean(velocidade),n()) -> data_plot
plot1 = plot_ly(data= data_plot, x= ~bairro_saida, y= ~velocidade_media, type = 'bar')
df %>%
  group_by(bairro_chegada) %>%
  summarize(velocidade_media = mean(velocidade),n()) -> data_plot
plot2 = plot_ly(data= data_plot, x= ~bairro_chegada, y= ~velocidade_media, type = 'bar')
subplot(plot1, plot2, shareY = T)

Plotar correlação passageiros tempo

p1 = plot_ly(data= df, x= ~passenger_count, y= ~trip_duration, type = 'scatter', mode = 'markers') 
p2 = plot_ly(data= df, x= ~dist_manhattan, y= ~trip_duration, type = 'scatter', mode = 'markers') %>% 
  layout(title="Correlacao Num. Passageiros vs. Tempo   |   Correlacao Distancia vs. Tempo")
subplot(p1, p2)

Média da velocidade das viagens por hora e dia da semana

df %>%
  group_by(pickup_hour) %>%
  summarize(velocidade_media = mean(velocidade),n()) -> data_plot1
plot1 = plot_ly(data= data_plot1, x= ~pickup_hour, y= ~velocidade_media, type = 'scatter', mode='lines')
df %>%
  group_by(pickup_weekdays) %>%
  summarize(velocidade_media = mean(velocidade),n()) -> data_plot2
plot2 = plot_ly(data= data_plot2, x= ~pickup_weekdays, y= ~velocidade_media, type = 'scatter', mode='lines') %>% 
  layout(title="Horas       |        Dias da Semana") 
subplot(plot1, plot2, shareY = T)

Quantidade de viagens por hora e dia da semana

df %>%
  group_by(pickup_hour) %>%
  count() -> data_plot1
plot1 = plot_ly(data= data_plot1, x= ~pickup_hour, y= ~n, type = 'bar')
df %>%
  group_by(pickup_weekdays) %>%
  count() -> data_plot2
plot2 = plot_ly(data= data_plot2, x= ~pickup_weekdays, y= ~n, type = 'bar') %>% 
  layout(title="Horas       |        Dias da Semana") 
subplot(plot1, plot2)

Plota mapa de calor de New York com ponto de partida da viagem

heat_map_taxi(train, "pickup")
OGR data source with driver: ESRI Shapefile 
Source: "C:\Users\Bruno Aquino\Documents\Trabalho R\v2\analise_taxi\data_source\mapa_ny", layer: "geo_export_8661594b-4f67-485f-8af1-84a4bd06054d"
with 5 features
It has 4 fields

Plota mapa de calor de New York com ponto de chegada da viagem

heat_map_taxi(train, "dropoff")
OGR data source with driver: ESRI Shapefile 
Source: "C:\Users\Bruno Aquino\Documents\Trabalho R\v2\analise_taxi\data_source\mapa_ny", layer: "geo_export_8661594b-4f67-485f-8af1-84a4bd06054d"
with 5 features
It has 4 fields

Normalizar dados para o modelo

Primeiro, criando variáveis dummies para dia da semana e hora

rr week_dummy = dummy(df\(pickup_weekdays, sep='_') hour_dummy = dummy(df\)pickup_hour, sep=‘’) bairro_dummy = dummy(df$bairro_chegada, sep=’’) df = data.frame(cbind(df, week_dummy, hour_dummy, bairro_dummy))

Agora normalizando com Min Max Scaler as variáveis: distância, trip_duration e passenger_count

rr df\(dist_manhattan = rescale(df\)dist_manhattan) df\(trip_duration = rescale(df\)trip_duration) df\(passenger_count = rescale(df\)trip_duration)

define X (train features) e y (target) para o treino

rr X <- df[c(‘passenger_count’, ‘dist_manhattan’, ‘pickup_weekdays_1’, ‘pickup_weekdays_2’, ‘pickup_weekdays_3’, ‘pickup_weekdays_4’, ‘pickup_weekdays_5’, ‘pickup_weekdays_6’, ‘pickup_weekdays_7’, ‘pickup_hour_1’, ‘pickup_hour_2’, ‘pickup_hour_3’, ‘pickup_hour_4’, ‘pickup_hour_5’, ‘pickup_hour_6’, ‘pickup_hour_7’, ‘pickup_hour_8’, ‘pickup_hour_9’, ‘pickup_hour_10’, ‘pickup_hour_11’, ‘pickup_hour_12’, ‘pickup_hour_13’, ‘pickup_hour_14’, ‘pickup_hour_15’, ‘pickup_hour_16’, ‘pickup_hour_17’, ‘pickup_hour_18’, ‘pickup_hour_19’, ‘pickup_hour_20’, ‘pickup_hour_21’, ‘pickup_hour_22’, ‘pickup_hour_23’, ‘bairro_chegada_1’, ‘bairro_chegada_2’, ‘bairro_chegada_3’, ‘bairro_chegada_4’, ‘bairro_chegada_5’, ‘bairro_chegada_6’, ‘bairro_chegada_7’, ‘bairro_chegada_8’, ‘bairro_chegada_9’)] y <- df[‘trip_duration’]

Cria modelo random forest para previsão de tempo de duração das viagens (trip_duration)

rr f = create_formula(X) fit <- randomForest(f, df, importance=TRUE, ntree=200) varImpPlot(fit)

LS0tDQp0aXRsZTogIlRyYWJhbGhvIGZpbmFsIGRlIFIgZG8gZ3J1cG86IEFkcmlhbmEsIEJydW5vLCBSYWZhZWwgZSBWaW7tY2l1cyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpgYGB7cn0NCmxpYnJhcnkobWFncml0dHIpICMgcXVhbmRvIGRlciBwcm9ibGVtYSBjb20gbyAiJT4lIg0KbGlicmFyeSgnZHBseXInKSAjIHNlbGVjYW8gZSBmaWx0cm8gZGUgZGFkb3MNCmxpYnJhcnkoJ2dlb3NwaGVyZScpICMgbG9jYWxpemFjYW8gZ2VvZXNwYWNpYWwNCmxpYnJhcnkobHVicmlkYXRlKSAjIGRhdGFzLCBmdW7n9WVzIGhvdXIsIG1vbnRoLCB3ZGF5DQpsaWJyYXJ5KHBsb3RseSkgIyBwbG90IGRvcyBncuFmaWNvcw0KbGlicmFyeShrbml0cikgIyB1c2FkYSBwZWxvIHBsb3RseQ0KbGlicmFyeShkdW1taWVzKSAjIGNyaWEgY29sdW5hcyBiaW7hcmlhcyBwYXJhIHZhcmnhdmVpcyBjYXRlZ/NyaWNhcw0KbGlicmFyeShzY2FsZXMpICMgbm9ybWFsaXphIGRhZG9zIHJlc2NhbGFuZG8gcGFyYSBmbG9hdCBkZSAwIGEgMQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpICMgY3JpYSByZWRlIG5ldXJhbCBwYXJhIGNyaWFyIHJlZ3Jlc3PjbyBkZSB0ZW1wbyBkZSB2aWFnZW0NCnNvdXJjZSgncHJlcHJvY2Vzc2luZy5SJykNCnNvdXJjZSgnbWFwYV9jYWxvcl9ueS5SJykNCmBgYA0KDQoNCmBgYHtyfQ0KdHJhaW4gPC0gcmVhZC5jc3YoImRhdGFfc291cmNlL3RyYWluLmNzdiIpDQoNCiMgcGVnYSBhcGVuYXMgYXMgMTAgcHJpbWVpcmFzIGxpbmhhcyBwYXJhIHRlc3RlDQpkZiA8LSBoZWFkKHRyYWluLCAxMDAwMCkNCmBgYA0KDQojIFByZXBhcmHn428gZG9zIGRhZG9zDQoNCiMjIFJlZ2njbyBkZSBzYe1kYQ0KYGBge3J9DQpkZiRiYWlycm9fc2FpZGEgPSBtYXBwbHkoZGVmaW5lX2JhaXJybywgZGYkcGlja3VwX2xvbmdpdHVkZSwgZGYkcGlja3VwX2xhdGl0dWRlKQ0KZGYkYmFpcnJvX2NoZWdhZGEgPSBtYXBwbHkoZGVmaW5lX2JhaXJybywgZGYkZHJvcG9mZl9sb25naXR1ZGUsIGRmJGRyb3BvZmZfbGF0aXR1ZGUpDQoNCmBgYA0KDQojIyBBZGljaW9uYSBkaXN04m5jaWEgRXVjbGlkaWFuYSBjYWxjdWxhZGEgYSBwYXJ0aXIgZGFzIGNvb3JkZW5hZGFzIChhcnF1aXZvIFByZXByb2Nlc3NpbmcuUikNCmBgYHtyfQ0KDQpkZiRkaXN0X2V1Y2xpZGlhbmEgPSBkaXN0X2V1Y2woZGYpDQoNCmBgYA0KDQoNCg0KIyMgQWRpY2lvbmEgZGlzdOJuY2lhIGRlIE1hbmhhdHRhbiBjYWxjdWxhZGEgYSBwYXJ0aXIgZGFzIGNvb3JkZW5hZGFzIChhcnF1aXZvIFByZXByb2Nlc3NpbmcuUikNCmBgYHtyfQ0KDQpkZiRkaXN0X21hbmhhdHRhbiA9IGRpc3RfbWFuaChkZikNCmRmJHZlbG9jaWRhZGUgPSBkZiRkaXN0X21hbmhhdHRhbiAvIGRmJHRyaXBfZHVyYXRpb24NCmBgYA0KDQojIyBQcmVwYXJhIGRhdGEgZSBob3JhIGRhIHBhcHJ0aWRhIChhY2hvIHF1ZSBu428gcHJlY2lzYSBkYSBjaGVnYWRhKQ0KIyMgQ29tIGlzc28g6SBwb3Nz7XZlbCBwZWdhciBob3LhcmlvIGRlIHBpY28gZSBkaWEgZGEgc2VtYW5hDQoNCmBgYHtyfQ0KZGYkcGlja3VwX2hvdXIgPC0gaG91cihkZiRwaWNrdXBfZGF0ZXRpbWUpDQpkZiRwaWNrdXBfbW9udGggPC0gbW9udGgoZGYkcGlja3VwX2RhdGV0aW1lKQ0KZGYkcGlja3VwX3dlZWtkYXlzIDwtIHdkYXkoZGYkcGlja3VwX2RhdGV0aW1lKQ0KYGBgDQoNCiMjIExpbXBlemEgZGUgY29ycmlkYXMgemVyYWRhcyBlIGxpbXBhIGNvcnJpZGFzIG11aXRvIGxvbmdhcw0KYGBge3J9DQpkZiAlPiUNCiAgZmlsdGVyKGRmJGRpc3RfbWFuaGF0dGFuID4gMC41KSAtPiBkZg0KZGYgJT4lDQogIGZpbHRlcihkZiR0cmlwX2R1cmF0aW9uIDwgMTAwMDApIC0+IGRmDQpgYGANCg0KIyBBbuFsaXNlcyBkZXNjcml0aXZhcw0KDQojIyBEaXZpc+NvIGRhcyByZWdp9WVzIHF1ZSBzZXBhcmFtb3MgZW0gTlk6DQohW1JlZ2n1ZXMgTlldKHJlZ2lvZXNfbnkuanBnKQ0KDQoNCiMjIFF1YW50aWRhZGUgZGUgdmlhZ2VucyBwb3IgcmVnaeNvIGRlIHNh7WRhIGUgY2hlZ2FkYQ0KYGBge3J9DQpkZiAlPiUNCiAgZ3JvdXBfYnkoYmFpcnJvX3NhaWRhKSAlPiUNCiAgY291bnQoKSAtPiBkYXRhX3Bsb3QNCnBsb3QxID0gcGxvdF9seShkYXRhPSBkYXRhX3Bsb3QsIHg9IH5iYWlycm9fc2FpZGEsIHk9IH5uLCB0eXBlID0gJ2JhcicpDQoNCmRmICU+JQ0KICBncm91cF9ieShiYWlycm9fY2hlZ2FkYSkgJT4lDQogIGNvdW50KCkgLT4gZGF0YV9wbG90DQpwbG90MiA9IHBsb3RfbHkoZGF0YT0gZGF0YV9wbG90LCB4PSB+YmFpcnJvX2NoZWdhZGEsIHk9IH5uLCB0eXBlID0gJ2JhcicpDQoNCnN1YnBsb3QocGxvdDEsIHBsb3QyLCBzaGFyZVkgPSBUKQ0KYGBgDQojIyBN6WRpYSBkYSB2ZWxvY2lkYWRlIGRhcyB2aWFnZW5zIHBvciByZWdp428gZGUgc2HtZGEgZSBjaGVnYWRhDQpgYGB7cn0NCmRmICU+JQ0KICBncm91cF9ieShiYWlycm9fc2FpZGEpICU+JQ0KICBzdW1tYXJpemUodmVsb2NpZGFkZV9tZWRpYSA9IG1lYW4odmVsb2NpZGFkZSksbigpKSAtPiBkYXRhX3Bsb3QNCnBsb3QxID0gcGxvdF9seShkYXRhPSBkYXRhX3Bsb3QsIHg9IH5iYWlycm9fc2FpZGEsIHk9IH52ZWxvY2lkYWRlX21lZGlhLCB0eXBlID0gJ2JhcicpDQoNCmRmICU+JQ0KICBncm91cF9ieShiYWlycm9fY2hlZ2FkYSkgJT4lDQogIHN1bW1hcml6ZSh2ZWxvY2lkYWRlX21lZGlhID0gbWVhbih2ZWxvY2lkYWRlKSxuKCkpIC0+IGRhdGFfcGxvdA0KcGxvdDIgPSBwbG90X2x5KGRhdGE9IGRhdGFfcGxvdCwgeD0gfmJhaXJyb19jaGVnYWRhLCB5PSB+dmVsb2NpZGFkZV9tZWRpYSwgdHlwZSA9ICdiYXInKQ0KDQpzdWJwbG90KHBsb3QxLCBwbG90Miwgc2hhcmVZID0gVCkNCmBgYA0KDQoNCiMjIFBsb3RhciBjb3JyZWxh5+NvIHBhc3NhZ2Vpcm9zIHRlbXBvDQpgYGB7cn0NCnAxID0gcGxvdF9seShkYXRhPSBkZiwgeD0gfnBhc3Nlbmdlcl9jb3VudCwgeT0gfnRyaXBfZHVyYXRpb24sIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbWFya2VycycpIA0KcDIgPSBwbG90X2x5KGRhdGE9IGRmLCB4PSB+ZGlzdF9tYW5oYXR0YW4sIHk9IH50cmlwX2R1cmF0aW9uLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnKSAlPiUgDQogIGxheW91dCh0aXRsZT0iQ29ycmVsYWNhbyBOdW0uIFBhc3NhZ2Vpcm9zIHZzLiBUZW1wbyAgIHwgICBDb3JyZWxhY2FvIERpc3RhbmNpYSB2cy4gVGVtcG8iKQ0Kc3VicGxvdChwMSwgcDIpDQpgYGANCg0KIyMgTelkaWEgZGEgdmVsb2NpZGFkZSBkYXMgdmlhZ2VucyBwb3IgaG9yYSBlIGRpYSBkYSBzZW1hbmENCmBgYHtyfQ0KZGYgJT4lDQogIGdyb3VwX2J5KHBpY2t1cF9ob3VyKSAlPiUNCiAgc3VtbWFyaXplKHZlbG9jaWRhZGVfbWVkaWEgPSBtZWFuKHZlbG9jaWRhZGUpLG4oKSkgLT4gZGF0YV9wbG90MQ0KcGxvdDEgPSBwbG90X2x5KGRhdGE9IGRhdGFfcGxvdDEsIHg9IH5waWNrdXBfaG91ciwgeT0gfnZlbG9jaWRhZGVfbWVkaWEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGU9J2xpbmVzJykNCmRmICU+JQ0KICBncm91cF9ieShwaWNrdXBfd2Vla2RheXMpICU+JQ0KICBzdW1tYXJpemUodmVsb2NpZGFkZV9tZWRpYSA9IG1lYW4odmVsb2NpZGFkZSksbigpKSAtPiBkYXRhX3Bsb3QyDQpwbG90MiA9IHBsb3RfbHkoZGF0YT0gZGF0YV9wbG90MiwgeD0gfnBpY2t1cF93ZWVrZGF5cywgeT0gfnZlbG9jaWRhZGVfbWVkaWEsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGU9J2xpbmVzJykgJT4lIA0KICBsYXlvdXQodGl0bGU9IkhvcmFzICAgICAgIHwgICAgICAgIERpYXMgZGEgU2VtYW5hIikgDQoNCnN1YnBsb3QocGxvdDEsIHBsb3QyLCBzaGFyZVkgPSBUKQ0KYGBgDQoNCiMjIFF1YW50aWRhZGUgZGUgdmlhZ2VucyBwb3IgaG9yYSBlIGRpYSBkYSBzZW1hbmENCmBgYHtyfQ0KZGYgJT4lDQogIGdyb3VwX2J5KHBpY2t1cF9ob3VyKSAlPiUNCiAgY291bnQoKSAtPiBkYXRhX3Bsb3QxDQpwbG90MSA9IHBsb3RfbHkoZGF0YT0gZGF0YV9wbG90MSwgeD0gfnBpY2t1cF9ob3VyLCB5PSB+biwgdHlwZSA9ICdiYXInKQ0KZGYgJT4lDQogIGdyb3VwX2J5KHBpY2t1cF93ZWVrZGF5cykgJT4lDQogIGNvdW50KCkgLT4gZGF0YV9wbG90Mg0KcGxvdDIgPSBwbG90X2x5KGRhdGE9IGRhdGFfcGxvdDIsIHg9IH5waWNrdXBfd2Vla2RheXMsIHk9IH5uLCB0eXBlID0gJ2JhcicpICU+JSANCiAgbGF5b3V0KHRpdGxlPSJIb3JhcyAgICAgICB8ICAgICAgICBEaWFzIGRhIFNlbWFuYSIpIA0KDQpzdWJwbG90KHBsb3QxLCBwbG90MikNCmBgYA0KDQojIyBQbG90YSBtYXBhIGRlIGNhbG9yIGRlIE5ldyBZb3JrIGNvbSBwb250byBkZSBwYXJ0aWRhIGRhIHZpYWdlbQ0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmhlYXRfbWFwX3RheGkodHJhaW4sICJwaWNrdXAiKQ0KYGBgDQoNCiMjIFBsb3RhIG1hcGEgZGUgY2Fsb3IgZGUgTmV3IFlvcmsgY29tIHBvbnRvIGRlIGNoZWdhZGEgZGEgdmlhZ2VtDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KaGVhdF9tYXBfdGF4aSh0cmFpbiwgImRyb3BvZmYiKQ0KYGBgDQoNCiMgTm9ybWFsaXphciBkYWRvcyBwYXJhIG8gbW9kZWxvDQoNCiMjIFByaW1laXJvLCBjcmlhbmRvIHZhcmnhdmVpcyBkdW1taWVzIHBhcmEgZGlhIGRhIHNlbWFuYSBlIGhvcmENCmBgYHtyfQ0Kd2Vla19kdW1teSA9IGR1bW15KGRmJHBpY2t1cF93ZWVrZGF5cywgc2VwPSdfJykNCmhvdXJfZHVtbXkgPSBkdW1teShkZiRwaWNrdXBfaG91ciwgc2VwPSdfJykNCmJhaXJyb19kdW1teSA9IGR1bW15KGRmJGJhaXJyb19jaGVnYWRhLCBzZXA9J18nKQ0KDQpkZiA9IGRhdGEuZnJhbWUoY2JpbmQoZGYsIHdlZWtfZHVtbXksIGhvdXJfZHVtbXksIGJhaXJyb19kdW1teSkpDQpgYGANCg0KIyMgQWdvcmEgbm9ybWFsaXphbmRvIGNvbSBNaW4gTWF4IFNjYWxlciBhcyB2YXJp4XZlaXM6IGRpc3TibmNpYSwgdHJpcF9kdXJhdGlvbiBlIHBhc3Nlbmdlcl9jb3VudA0KYGBge3J9DQpkZiRkaXN0X21hbmhhdHRhbiA9IHJlc2NhbGUoZGYkZGlzdF9tYW5oYXR0YW4pDQpkZiR0cmlwX2R1cmF0aW9uID0gcmVzY2FsZShkZiR0cmlwX2R1cmF0aW9uKQ0KZGYkcGFzc2VuZ2VyX2NvdW50ID0gcmVzY2FsZShkZiR0cmlwX2R1cmF0aW9uKQ0KDQpgYGANCg0KIyMgZGVmaW5lIFggKHRyYWluIGZlYXR1cmVzKSBlIHkgKHRhcmdldCkgcGFyYSBvIHRyZWlubw0KYGBge3J9DQpYIDwtIGRmW2MoJ3Bhc3Nlbmdlcl9jb3VudCcsICdkaXN0X21hbmhhdHRhbicsICdwaWNrdXBfd2Vla2RheXNfMScsICdwaWNrdXBfd2Vla2RheXNfMicsICdwaWNrdXBfd2Vla2RheXNfMycsDQogICAgICAgICAncGlja3VwX3dlZWtkYXlzXzQnLCAncGlja3VwX3dlZWtkYXlzXzUnLCAncGlja3VwX3dlZWtkYXlzXzYnLCAncGlja3VwX3dlZWtkYXlzXzcnLCAncGlja3VwX2hvdXJfMScsDQogICAgICAgICAncGlja3VwX2hvdXJfMicsICdwaWNrdXBfaG91cl8zJywgJ3BpY2t1cF9ob3VyXzQnLCAncGlja3VwX2hvdXJfNScsICdwaWNrdXBfaG91cl82JywgJ3BpY2t1cF9ob3VyXzcnLA0KICAgICAgICAgJ3BpY2t1cF9ob3VyXzgnLCAncGlja3VwX2hvdXJfOScsICdwaWNrdXBfaG91cl8xMCcsICdwaWNrdXBfaG91cl8xMScsICdwaWNrdXBfaG91cl8xMicsDQogICAgICAgICAncGlja3VwX2hvdXJfMTMnLCAncGlja3VwX2hvdXJfMTQnLCAncGlja3VwX2hvdXJfMTUnLCAncGlja3VwX2hvdXJfMTYnLCAncGlja3VwX2hvdXJfMTcnLA0KICAgICAgICAgJ3BpY2t1cF9ob3VyXzE4JywgJ3BpY2t1cF9ob3VyXzE5JywgJ3BpY2t1cF9ob3VyXzIwJywgJ3BpY2t1cF9ob3VyXzIxJywgJ3BpY2t1cF9ob3VyXzIyJywNCiAgICAgICAgICdwaWNrdXBfaG91cl8yMycsICdiYWlycm9fY2hlZ2FkYV8xJywgJ2JhaXJyb19jaGVnYWRhXzInLCAnYmFpcnJvX2NoZWdhZGFfMycsICdiYWlycm9fY2hlZ2FkYV80JywNCiAgICAgICAgICdiYWlycm9fY2hlZ2FkYV81JywgJ2JhaXJyb19jaGVnYWRhXzYnLCAnYmFpcnJvX2NoZWdhZGFfNycsICdiYWlycm9fY2hlZ2FkYV84JywgJ2JhaXJyb19jaGVnYWRhXzknKV0NCg0KeSA8LSBkZlsndHJpcF9kdXJhdGlvbiddDQpgYGANCg0KIyMgQ3JpYSBtb2RlbG8gcmFuZG9tIGZvcmVzdCBwYXJhIHByZXZpc+NvIGRlIHRlbXBvIGRlIGR1cmHn428gZGFzIHZpYWdlbnMgKHRyaXBfZHVyYXRpb24pDQpgYGB7cn0NCmYgPSBjcmVhdGVfZm9ybXVsYShYKQ0KZml0IDwtIHJhbmRvbUZvcmVzdChmLCBkZiwgaW1wb3J0YW5jZT1UUlVFLCBudHJlZT0yMDApDQp2YXJJbXBQbG90KGZpdCkNCmBgYA0KDQoNCg==